/*
* Copyright (c) 2021,
* All rights reserved.
*
* 文件名称：base_server.c
* 创建日期：2021/12/19
* 摘 要：基础服务接口实现
*
* 当前版本：1.0
* 作 者：赵满刚
* 完成日期：
*/


#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <time.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>

#include "../public/write_log.h"
#include "../public/cJSON.h"
#include "../public/protocol.h"
#include "base_server.h"
#include "db_oper.h"


/*
*name:write_data_to_socket_fd(void* data,int data_len)
*inputparam:data,data_len
*return: -1:deal error other:send bit nums 
*describe:write to fd,if write success return bit nums.else return -1
*/
static int write_data_to_socket_fd(int fd, void* data, int data_len)
{
    if (NULL == data || 0 == data_len)
    {
        return -1;
    }
    return write(fd, data, data_len);
    return 0;
}

/**
 * \brief 构造用户信息包
 * 
 * \param [out]	packet 构造好的数据
 * \param [in]  user_conn 用户连接信息
 * \return int  -1 失败 other-成功
 */
static int construct_userinfo_packet(USER_CONNECT_INFO* user_conn, char* packet)
{
    if (NULL == user_conn|| NULL == packet)
    {
        return FAIL;
    }

    cJSON* userinfo = NULL;
    userinfo = cJSON_CreateObject();

    cJSON_AddNumberToObject(userinfo, STR_TYPE, PROTO_TYPE_USERINFO);    //add packet type to json
    cJSON_AddNumberToObject(userinfo, STR_ID, user_conn->user_id);
    cJSON_AddStringToObject(userinfo, STR_USER_NAME, user_conn->user_name);    //add username to json
    cJSON_AddStringToObject(userinfo,STR_ICON, user_conn->icon);    //add icon to json
    write_log(LOG_DEBUG,"%s",cJSON_Print(userinfo)); 
    strncpy(packet,cJSON_Print(userinfo),MAXLINE); //get string from json
    write_log(LOG_DEBUG,"[%d]%s",strlen(packet),packet);
    return SUCCESS;
}


/**
 * \brief 登录请求处理
 * 
 * \param [in]	json 请求数据
 * \param [in]  user_conn 用户连接信息
 * \return int  -1 失败 other-成功
 */
static int request_deal_login(cJSON* json, USER_CONNECT_INFO* user_conn)
{
    
    /*analyze password and id*/
    char password[MAX_LEN_PASSWORD] = {0};
    cJSON *node = NULL;
    node = cJSON_GetObjectItem(json, STR_USER_NAME);
    if (NULL == node)
    {
        //not find id
        write_log(LOG_WARNING,"not find id");
        return FAIL;
    }
    else
    {
        if (node->type == cJSON_String)
        {
            strncpy(user_conn->user_name, node->valuestring, MAX_LEN_USERNAME);
            write_log(LOG_INFO, "username:%s", user_conn->user_name);
        }
    }
    node = NULL;
    node = cJSON_GetObjectItem(json, STR_PASSWORD);
    if (NULL == node)
    {
        //not find password
        write_log(LOG_WARNING,"not find password");
        return FAIL;
    }
    else
    {
        if (node->type == cJSON_String)
        {
            strncpy(password, node->valuestring, MAX_LEN_PASSWORD);
        }
    }

    char send_packet[MAXLINE] = {0};
    if (FAIL == verify_user_info_from_mysql(password, user_conn))
    {
        
    }
    else
    {
        construct_userinfo_packet(user_conn, send_packet);
    }

    write_log(LOG_DEBUG,"[len:%d]s->c:%s", strlen(send_packet), send_packet);
    write_data_to_socket_fd(user_conn->connect_fd, send_packet, strlen(send_packet));
}

/**
 * \brief 登录请求处理
 * 
 * \param [in]	json 请求数据
 * \param [in]  user_conn 用户连接信息
 * \return int  -1 失败 other-成功
 */
static int request_deal_file_req(cJSON* json, USER_CONNECT_INFO* user_conn)
{
    if (NULL == json || NULL == user_conn)
    {
        write_log(LOG_ERROR, "param is NULL");
        return FAIL;
    }
    char send_packet[MAXLINE] = {0};
    get_user_file_from_mysql(user_conn, send_packet);
    write_log(LOG_DEBUG,"[len:%d]s->c:%s", strlen(send_packet), send_packet);
    write_data_to_socket_fd(user_conn->connect_fd, send_packet, strlen(send_packet));
}

/**
 * \brief 请求解析并响应
 * 
 * \param [in]	buff 请求数据
 * \param [in]  len  buff数据长度
 * \param [in]  user_conn 用户连接信息
 * \return int  -1 失败 other-成功
 */
static int request_parse_rsp(char* buff, int len, USER_CONNECT_INFO* user_conn)
{
    if (NULL == buff || 0 == len || NULL == user_conn)
    {
        return FAIL;
    }

    //从buff中解析json结构
    cJSON *json = cJSON_Parse(buff);
    cJSON *node = NULL;
    //cJSON_GetObjectItem 根据key来查找json节点，如果有返回非空
    node = cJSON_GetObjectItem(json,"type");
    int type = -1;
    if (NULL == node)
    {
        //get message type failed;
        write_log(LOG_ERROR,"json node type is null");
    }
    else
    {
        if (node->type == cJSON_Number)
        {
            //get message type success
            type = node->valueint;
            write_log(LOG_INFO, "type:%d",node->valueint);
        }
    }

    switch (type)
    {
    case PROTO_TYPE_LOGIN:  /*登录请求*/
        write_log(LOG_DEBUG, "login req");
        request_deal_login(json, user_conn);
        break;
    
    case PROTO_TYPE_FILE_REQ:   /* 文件列表请求 */
        request_deal_file_req(json, user_conn);
        break;
    default:
        break;
    }

    return SUCCESS;
}



/**
 * \brief 用户参数处理
 * 
 * \param [in]	argc 参数数量
 * \param [in]	argv 参数内容
 * \param [in]  address_len 地址字符串最大长度
 * \param [out] address 监听地址
 * \param [out] port 监听端口
 * \return void
 */
void user_opt_deal(int argc, char* argv[], char* address, int address_len, int *port)
{
    char ch = '\0';

    while ((ch = getopt(argc, argv, "a::p::dDhH")) != -1)
    {
        switch (ch)
        {
        case 'a':   /* 监听地址 */
            /* code */
            break;
        case 'p': /* 监听端口 */

            break; /* code */
        
        case 'd': /* 后台运行-WARNING以上日志计入文件 */
            debug_level_set(LOG_WARNING);
            debug_file_set("./");
            write_log(LOG_INFO, "run in background");
            break;
        
        case 'D': /* 前台运行-打印所有日志 */
            debug_level_set(LOG_MAX);
            write_log(LOG_INFO, "run in shell");
            break;
            
        case 'h': /* 帮助 */
            printf("%s", HELP_MSG);
            break;
        default:
            printf("%s", HELP_MSG);
            break;
        }
    }
}




/**
 * \brief 用户请求处理
 * 
 * \param [in]	connect_fd 连接套接字
 * \return void
 */
void client_request_deal(int connect_fd)
{

    ssize_t n;
    char buff[MAXLINE] = {0};
    USER_CONNECT_INFO* user_conn = NULL;
    user_conn = malloc(sizeof(USER_CONNECT_INFO));
    memset(user_conn, 0, sizeof(USER_CONNECT_INFO));
    if (NULL == user_conn)
    {
       //malloc error 
       write_log(LOG_ERROR,"malloc error");
       user_conn = NULL;
       return;
    }
    user_conn->connect_fd = connect_fd;
    struct sockaddr_storage sock_cli;
    socklen_t len = -1;

    len = sizeof(sock_cli);

    if (getpeername(connect_fd,(struct sockaddr*)&sock_cli,&len) < 0)
    {
        write_log(LOG_ERROR,"get client source info error");
    }
    else
    {
        struct sockaddr_in *in = (struct sockaddr_in *)&sock_cli;
        inet_ntop(AF_INET,&in->sin_addr,user_conn->source_ip,sizeof(user_conn->source_ip)); //ip to xxx.xxx.xxx.xxx
        user_conn->source_port = htons(in->sin_port);
        write_log(LOG_INFO, "source ip:%s", user_conn->source_ip);
        write_log(LOG_INFO, "source port:%d", user_conn->source_port);
    }
    
    while (1)
    {
        n = read(connect_fd, buff, MAXLINE-2);
        if (n == 0)
        {
            //close connect by cient 
            write_log(LOG_INFO,"close by client");
            if (-1 == close(connect_fd))
            {
                //close connect_fd fail;
                write_log(LOG_ERROR,"close connect fail,child pid is %d",getpid());
            }
            //logout_deal(chat_conn);
            break;  //end child'
        }
        else if (n > 0)
        {
            //recv data success ,n bit data;
            buff[n+1] = '\0';
            write_log(LOG_DEBUG,"recv data:%s", buff);
            if (-1 == request_parse_rsp(buff, n, user_conn))
            {
                
            }
            memset(buff,0,sizeof(buff));
        }
        else 
        {
            //other error;
            write_log(LOG_ERROR,"recv other error in child %d",getpid());
            if (-1 == close(connect_fd))
            {
                write_log(LOG_ERROR,"close connect_fd fail,childpid is %d",getpid());
            }
            //logout_deal(chat_conn);
            break;
        }
    }//while
}